#include "gdbinfo.h"
#include "protocol.h"
#include "xip.h"
#include "xicmp.h"
#include "xudp.h"
#include "xtcp.h"


void xip_init(void){

    // 没有啥需要初始化的

};

void xip_in(xnet_packet_t *packet)
{
    xip_hdr_t *ip_hdr = (xip_hdr_t *)packet->data;
    uint32_t header_size, total_size;
    xipaddr_t src_ip;
    uint16_t pre_checksum;

    // ipv4版本
    if (ip_hdr->version != XNET_VERSION_IPV4)
    {
        return;
    }
    header_size = ip_hdr->hdr_len * 4;
    total_size = swap_order16(ip_hdr->total_len);

    if (header_size < sizeof(ip_hdr) || total_size < header_size)
    {
        return; // 包有损坏
    }

    pre_checksum = ip_hdr->header_checksum;
    ip_hdr->header_checksum = 0; // 先置0
    if (pre_checksum != checksum16((uint16_t *)ip_hdr, header_size, 0, 1))
    {
        if (IP_GDB_INFO)
            print_info("receive a wrong ip packet , wrong checksum");
        return;
    }
    ip_hdr->header_checksum = pre_checksum;

    if (!xipaddr_is_equal_buf(my_ip, ip_hdr->dest_ip))
    {
        return;
    }

    src_ip.addr = *(uint32_t *)(ip_hdr->src_ip);

    if (IP_GDB_INFO)
    {
        char ip_str[32];
        ip_to_str(ip_hdr->src_ip, ip_str, XNET_IPV4_ADDR_SIZE);
        print_info("get a ip packet from %s", ip_str);
    }

    switch (ip_hdr->protocol)
    {
    case XNET_PROTOCOL_ICMP:
        remove_header(packet, header_size);
        xicmp_in(&src_ip, packet);
        break;
    case XNET_PROTOCOL_UDP:
        if(packet->size >= sizeof(xudp_hdr_t)){
            xudp_hdr_t *udp_hdr=(xudp_hdr_t *)(packet->data+header_size);
            xudp_t *udp=xudp_find(swap_order16(udp_hdr->dest_port));
            if(udp){
                remove_header(packet, header_size);
                xudp_in(udp, &src_ip, packet);
            }else{
                xicmp_dest_unreach(XICMP_CODE_PORT_UNREACH, ip_hdr);
            }
        }
        break;
    case XNET_PROTOCOL_TCP:
        remove_header(packet,header_size);
        xtcp_in(&src_ip,packet);
        break;

    default:
        break;
    }
}

xnet_err_t xip_out(xnet_protocol_t protocal, xipaddr_t *dest_ip, xnet_packet_t *packet)
{
    // 不支持转发，不支持分片的ip发送包的函数
    static uint32_t ip_packet_id = 0; // 每个ip包静态的id
    xip_hdr_t *ip_hdr;
    add_header(packet, sizeof(xip_hdr_t));

    ip_hdr = (xip_hdr_t *)packet->data;

    ip_hdr->version = XNET_VERSION_IPV4;
    ip_hdr->hdr_len = sizeof(xip_hdr_t) / 4;
    ip_hdr->type_of_service = 0x0;
    ip_hdr->total_len = swap_order16(packet->size);
    ip_hdr->identification = swap_order16(ip_packet_id);
    ip_packet_id++;
    ip_hdr->flags_fragmentoffset = 0;
    ip_hdr->time_to_live = XNET_IP_DEFAULT_TTL;
    ip_hdr->protocol = protocal;

    memcpy(ip_hdr->src_ip, my_ip.array, XNET_IPV4_ADDR_SIZE);
    memcpy(ip_hdr->dest_ip, dest_ip, XNET_IPV4_ADDR_SIZE);
    ip_hdr->header_checksum = 0;
    ip_hdr->header_checksum = checksum16((uint16_t *)ip_hdr, sizeof(xip_hdr_t), 0, 1);
    if (IP_GDB_INFO)
    {
        char ip_str[32];
        ip_to_str(ip_hdr->dest_ip, ip_str, XNET_IPV4_ADDR_SIZE);
        print_info("try to send a IP packet , size =0x%x , target ip %s", packet->size, ip_str);
    }

    return ethernet_out_to(XNET_PROTOCOL_IP, dest_ip, packet);
};

uint16_t checksum16(uint16_t *data, uint16_t len, uint16_t pre_sum, int complement)
{
    // 16位校验和函数，pre_sum表示之前的校验和，complement为1，取反，为0，不取反
    // len表示buf是多少个字节，当len为8时，表示buf是4个uint16
    uint32_t checksum = pre_sum;
    uint16_t high;

    while (len > 1)
    {
        checksum += *data;
        data++;
        len -= 2;
    }

    if (len > 0)
    {
        checksum += *(uint8_t *)data;
    }

    while ((high = (checksum >> 16)) != 0)
    {
        checksum = high + (checksum & 0xffff);
    }

    return (complement) ? (uint16_t)(~checksum) : (uint16_t)(checksum);
};

// udp tcp伪校验和校验算法
uint16_t checksum_peso(const xipaddr_t *src_ip, const xipaddr_t *dest_ip, uint8_t protocol, uint16_t *buf, uint16_t len){
    uint8_t zero_protocol[2]={0,protocol};
    uint16_t c_len=swap_order16(len);

    uint32_t sum=checksum16((uint16_t *)src_ip->array,XNET_IPV4_ADDR_SIZE,0,0);
    sum=checksum16((uint16_t *)dest_ip->array,XNET_IPV4_ADDR_SIZE,sum,0);
    sum=checksum16((uint16_t *)zero_protocol,2,sum,0);
    sum=checksum16((uint16_t *) &c_len,2,sum,0);
    return checksum16(buf,len,sum,1);

};